// exception.cc 
//	Entry point into the Nachos kernel from user programs.
//	There are two kinds of things that can cause control to
//	transfer back to here from user code:
//
//	syscall -- The user code explicitly requests to call a procedure
//	in the Nachos kernel.  Right now, the only function we support is
//	"Halt".
//
//	exceptions -- The user code does something that the CPU can't handle.
//	For instance, accessing memory that doesn't exist, arithmetic errors,
//	etc.  
//
//	Interrupts (which can also cause control to transfer from user
//	code into the Nachos kernel) are handled elsewhere.
//
// For now, this only handles the Halt() system call.
// Everything else core dumps.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "syscall.h"
#include <stdio.h>
#include "processTable.h"
#include <iostream>

#ifdef NETWORK
#include "network.h"
#define APPLINE 1
#define PICLINE 2
#define PASLINE 3
#define CASHIER 4
#define CUSTOMER 5 
int mailboxIndex = 2;
#endif

using namespace std;

//for keeping track of kernel threads
int kernalThreadIndex = 0;
int processIndex = 0;  
int currentTLBSlot = 0; //Iterator to go through TLB elements
Lock pfl("pfl"); //Page Fault Handler Lock - To ensure that no race condition would occur


/* 
for now code for converting int to char
*/
int myexp ( int count ) {
  int i, val=1;
  for (i=0; i<count; i++ ) {
    val = val * 10;
  }
  return val;
}

void itoa( char arr[], int size, int val ) {
  int i, max, dig, subval, loc;
  for (i=0; i<size; i++ ) {
    arr[i] = '\0';
  }

  for ( i=1; i<=2; i++ ) {
    if (( val / myexp(i) ) == 0 ) {
      max = i-1;
      break;
    }
  }

  subval = 0;
  loc = 0;
  for ( i=max; i>=0; i-- ) {
    dig = 48 + ((val-subval) / myexp(i));
    subval = (dig-48) * myexp(max);
    arr[loc] = dig;
    loc++;
  }

  return;
}




//NOTE: added code to make it work with a TLB
int copyin(unsigned int vaddr, int len, char *buf) {
  // Copy len bytes from the current thread's virtual address vaddr.
  // Return the number of bytes so read, or -1 if an error occors.
  // Errors can generally mean a bad virtual address was passed in.
  //printf("COPY IN FUNCTION\n");

  bool result;
  int n=0;			// The number of bytes copied in
  int *paddr = new int;
    
  int *tempPaddr = new int;
  if(currentThread->space->validateVaddr(vaddr)){
    
    //else just let it fail and return -1
    while ( n >= 0 && n < len) {
      
      //test it first and induce a page fault
      //here we are assuming that we will never be in here infinitely
      while(!machine->ReadMem(vaddr,1,tempPaddr)){
	//induce a page fault
	//printf("copyin loop\n");
      }
	    
      result = machine->ReadMem( vaddr, 1, paddr );
      buf[n++] = *paddr;

      if ( !result ) {
	//translation failed
	return -1;
      }

      vaddr++;
    }
    
  }
    
  else {
    printf("copyin() : vaddr %d is not valid!\n",vaddr);
  }

  delete paddr;
  return len;
}


int copyout(unsigned int vaddr, int len, char *buf) {
    // Copy len bytes to the current thread's virtual address vaddr.
    // Return the number of bytes so written, or -1 if an error
    // occors.  Errors can generally mean a bad virtual address was
    // passed in.
  //printf("In copyout() \n");
  bool result;
    int n=0;			// The number of bytes copied in

    while ( n >= 0 && n < len) {
      // Note that we check every byte's address
      result = machine->WriteMem( vaddr, 1, (int)(buf[n++]) );

      if ( !result ) {
	//translation failed
	return -1;
      }

      vaddr++;
    }

    return n;
}

void Create_Syscall(unsigned int vaddr, int len) {
    // Create the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  No
    // way to return errors, though...
    char *buf = new char[len+1];	// Kernel buffer to put the name in

    if (!buf) return;

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","Bad pointer passed to Create\n");
	delete buf;
	return;
    }

    buf[len]='\0';

    fileSystem->Create(buf,0);
    delete[] buf;
    return;
}

int Open_Syscall(unsigned int vaddr, int len) {
    // Open the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  If
    // the file is opened successfully, it is put in the address
    // space's file table and an id returned that can find the file
    // later.  If there are any errors, -1 is returned.
    char *buf = new char[len+1];	// Kernel buffer to put the name in
    OpenFile *f;			// The new open file
    int id;				// The openfile id

    if (!buf) {
	printf("%s","Can't allocate kernel buffer in Open\n");
	return -1;
    }

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","Bad pointer passed to Open\n");
	delete[] buf;
	return -1;
    }

    buf[len]='\0';

    f = fileSystem->Open(buf);
    delete[] buf;

    if ( f ) {
	if ((id = currentThread->space->fileTable.Put(f)) == -1 )
	    delete f;
	return id;
    }
    else
	return -1;
}

void Write_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one. For disk files, the file is looked
    // up in the current address space's open file table and used as
    // the target of the write.
  //printf("WRITE SYS CALL\n");
    char *buf;		// Kernel buffer for output
    OpenFile *f;	// Open file for output

    if ( id == ConsoleInput) return;
    
    if ( !(buf = new char[len]) ) {
	printf("%s","Error allocating kernel buffer for write!\n");
	return;
    } else {
        if ( copyin(vaddr,len,buf) == -1 ) {
	    printf("%s","Bad pointer passed to to write: data not written\n");
	    delete[] buf;
	    return;
	}
    }

    if ( id == ConsoleOutput) {
      for (int ii=0; ii<len; ii++) {
	printf("%c",buf[ii]);
      }

    } else {
	if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
	    f->Write(buf, len);
	} else {
	    printf("%s","Bad OpenFileId passed to Write\n");
	    len = -1;
	}
    }

    delete[] buf;
}

int Read_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one.    We reuse len as the number of bytes
    // read, which is an unnessecary savings of space.
  //printf("READ SYS CALL\n");
    char *buf;		// Kernel buffer for input
    OpenFile *f;	// Open file for output

    if ( id == ConsoleOutput) return -1;
    
    if ( !(buf = new char[len]) ) {
	printf("%s","Error allocating kernel buffer in Read\n");
	return -1;
    }

    if ( id == ConsoleInput) {
      //Reading from the keyboard
      scanf("%s", buf);

      if ( copyout(vaddr, len, buf) == -1 ) {
	printf("%s","Bad pointer passed to Read: data not copied\n");
      }
    } else {
	if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
	    len = f->Read(buf, len);
	    if ( len > 0 ) {
	        //Read something from the file. Put into user's address space
  	        if ( copyout(vaddr, len, buf) == -1 ) {
		    printf("%s","Bad pointer passed to Read: data not copied\n");
		}
	    }
	} else {
	    printf("%s","Bad OpenFileId passed to Read\n");
	    len = -1;
	}
    }

    delete[] buf;
    return len;
}

void Close_Syscall(int fd) {
    // Close the file associated with id fd.  No error reporting.
    OpenFile *f = (OpenFile *) currentThread->space->fileTable.Remove(fd);

    if ( f ) {
      delete f;
    } else {
      printf("%s","Tried to close an unopen file\n");
    }
}





/*
------------------------------
           Yield_Syscall:
-----------------------------
*/

void Yield_Syscall(){
  // * just call currentThread->Yield()
  currentThread->Yield();
  

}




/*
------------------------------
           exec_thread:
-----------------------------
*/


void exec_thread(){
  // * initialize the register by using currenThread->space
  currentThread->space->InitRegisters();
  // * Call RestoreState through currentThread->space.
  currentThread->space->RestoreState();
  // * call machine->Run()
  machine->Run();

}


/*
------------------------------
          EXEC SYSCALL:
-----------------------------
*/
int  Exec_Syscall(int vaddr, int len){ 
 
 
  int paddr;
  char *buf = new char[len];
  OpenFile *f;


  //use copyin to extract file name.
 if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","Exec_Syscall: Bad pointer passed to Open\n");
	delete buf;
	return -1;
      }

  // * Convert it to the physical address and read the contents from there , which will give you the name of the process to be executed.
  ExceptionType exception = machine->Translate(vaddr,&paddr,len,false);
  if(exception != NoException)
    {
      ExceptionHandler(exception);
    }
  
  // * Now Open that file using filesystem->Open.
  // * Store its openfile pointer.
  f = fileSystem->Open(buf);
  if(f == NULL){
    printf ("Unable to open file %s\n",buf);
    return -1;
  }
  else printf("Exec_Syscall: FILE %s OPENED\n", buf);
 
   
  // * Create a new thread.
  Thread *processThread = new Thread("PROCESS THREAD");
  // * Create new addrespace for this executable file.
  AddrSpace *space = new AddrSpace(f);
  //printf("address space : %d\n",space);
   // * Allocate the space created to this thread's space.
  processThread->space = space;

  // * Update the process table and related data structures.
  Process *newprocess = new Process(space);
 

  //PTLock->Acquire();

  processTable->addProcess(newprocess);
  newprocess->addThread(processThread);
#ifdef NETWORK
  newprocess->setMailBox(mailboxIndex++);
#endif
  //PTLock->Release();
 


  // * Write the space ID to the register 2.
  //need to fix spaceID to something else
  //Space ID is suppose to refer to process ID, but since no child processes, just return 0 for now
  machine->WriteRegister(2,0);

  //* Fork the new thread. I call it exec_thread.
  processThread->Fork((VoidFunctionPtr)exec_thread,0);

  delete[] buf;
  return 0; 
  
}


/*
------------------------------
           kernel_thread:
-----------------------------
*/

//void kernel_thread(int vaddr){
void kernel_thread(int vaddr){

  // Initialize registers first
  currentThread->space->InitRegisters(); 

  // * write to the register PCReg the virtual address.
  machine->WriteRegister(PCReg,vaddr);


  // * write virtualaddress + 4 in NextPCReg
  machine->WriteRegister(NextPCReg,vaddr+4);


 // * Restorestate function in order to prevent information loss while context switching
  currentThread->space->RestoreState();


  // * write to the stack register, the starting position of the stack for this thread.
  //machine->WriteRegister(StackReg,8 * PageSize - 16);

  //PTLock->Acquire();
  Process *p = processTable->getProcess(currentThread->space);
  if(p == NULL)
    {
      printf("process is NULL\n");
    }
  //p->printThreads();
  ProcessTableThread* ptt =  p->getThread(currentThread);
  //p->printThreads();
  if(ptt != NULL)
    {
      machine->WriteRegister(StackReg,ptt->getStackReg());
    }

  else
    {
      printf("no ptt for this thread!\n");
    }

  //PTLock->Release();

  // it should be numPages*PageSize + StackSize - 16, where stacksize is 8 pages of memory
 
  // * Save the user state incase of a context switch
  currentThread->SaveUserState(); 

  // * call machine->Run()
  machine->Run();
}


/*
------------------------------
          FORK SYSCALL:
-----------------------------
*/

void Fork_Syscall(unsigned int vaddr){ 
  printf("IN fork syscall\n");
  char *buf = new char[6];
  char buf2[13] = "kernel      ";
 

  for(int i = 7; i< 13; i++)
    {
      buf2[i] = buf[i - 7];
    }
  // * Create a New thread. This would be a kernel thread.
  itoa(buf,6,kernalThreadIndex);
  Thread *kernelThread = new Thread(buf);
  // Thread *kernelThread = new Thread("kernal thread");
  kernalThreadIndex++;

 
  // * Update the Process Table for Multiprogramming part.


  Process *threadprocess = processTable->getProcess(currentThread->space);
  if(threadprocess == NULL){
    printf("FORKED PROCESS IS NULL!");
  }
  
  if(currentThread->space != threadprocess->getAddrSpace())
    {
      printf("Forking thread into wrong address space\n");
    }

  if(!currentThread->space->validateVaddr(vaddr))
    {
      printf("Forked virtual address out of range, halting\n");
      interrupt->Halt();
    }
    

  // * Allocate the addrespace to the thread being forked which is essentially current thread's addresspsace because threads share the process addressspace.

  kernelThread->space = currentThread->space;
  threadprocess->addThread(kernelThread);
  //threadprocess->printThreads();


  //---------------THE FOLLOWING CODE WAS FOR PROJECT 2 -------------------

  //threadprocess->printThreads();
  // * Now we need to allocate Stack Space for the new thread
  // First we find the pages that are available in memory, and let the thread's process keep track of which pages that the thread stack is using
  int *bitmapLoc = threadprocess->getThread(kernelThread)->getbitMapLoc();
  //threadprocess->printThreads();
  if(bitmapLoc == NULL){
    printf("ERROR IN FORK SYSCALL!");
  }

  for(int i = 0; i < 8; i++) {
    int loc = mainMemory->Find();
    
    if(loc != -1)
      {
	
	bitmapLoc[i] = loc;
	
      }
    else
      {
	//space->Clear(pp);
	printf("\n\nERROR: could not allocate space in MM for thread %s\n\n",kernelThread->getName());

	//Cannot allocate memory for new thread, thus halt Nachos.
	interrupt->Halt();
      }
  }
  
  MMLock->Release();
  //PTLock->Release();

  // Now we enlarge the size of the page table of the process to accomodate the stack space of the new thread.
  int numPages = currentThread->space->NewThreadStack(bitmapLoc);
  //threadprocess->printThreads();
  threadprocess->getThread(kernelThread)->setStackReg(numPages*PageSize - 16);
  //threadprocess->printThreads();
  //Fork the kernel thread
 
  kernelThread->Fork((VoidFunctionPtr)kernel_thread,vaddr);

  //--------------- PROJECT CODE 2 END -----------------

  //---------------- PROJECT 3 CODE

//   // Now we enlarge the size of the page table of the process to accomodate the stack space of the new thread.
//   //int numPages = currentThread->space->NewThreadStack(bitmapLoc);
//   int numPages = currentThread->space->NewThreadStack(NULL);
//   threadprocess->getThread(kernelThread)->setStackReg(numPages*PageSize - 16);
 

//   //Fork the kernel thread
//   kernelThread->Fork((VoidFunctionPtr)kernel_thread,vaddr);
}



/*
------------------------------
          EXIT SYSCALL:
-----------------------------
*/

void Exit_Syscall(int status){
  //#ifndef NETWORK
  //Running regularly without network

  DEBUG('a',"Exit Syscall with status %d.\n",status);
  //printf("\n%s: EXIT SYSCALL CALLED\n", currentThread->getName());

  //PTLock->Acquire();  
  Process *p = processTable->getProcess(currentThread->space);
  
  //is process returned NULL?
  if(p == NULL) 
    {
      printf("in Exit_Syscall(int status) getProcess returned NULL\n");
    }
  
  //first is it last awake thread of nachos?
  else if(processTable->getTotalNumActiveThreads() == 1)
    {
      printf("Last thread in nachos terminating\n");
      interrupt->Halt();
    }
   
  //second if not last thread in process 
  else if(processTable->getProcess(currentThread->space)->getNumActiveThreads() > 1)
    {
      printf("%s : not last thread in process terminating\n",currentThread->getName());  
      p->removeThread(currentThread);
      p->printThreads();
    }
   
  //third if the last thread in process
  else if(p->getNumActiveThreads() == 1)
    {
      printf("s% : last thread in process, terminating process\n",currentThread->getName());
      
		//-------------------- PROJECT 3 CODE ----------------

//       pfl.Acquire();

//       //currentThread->space->freeSwap();
//       ipt->clearProcessElements(currentThread->space);
//       processTable->removeProcess(currentThread->space);
      
//       pfl.Release();


//---------------------- PROJECT 2 CODE --------------------

		processTable->removeProcess(currentThread->space);

    }
   
  //fourth is the number of active threads in the process less than 1? not possible!
  else if(p->getNumActiveThreads()< 1)
    {
      printf("there MUST be at least one thread in process!!!!!\n");
    }
      
  //None of the cases fit
  else
    {
      printf("ERROR: did not fit any of the Cases!!\n");
    }

  //PTLock->Release();
 
  currentThread->Finish();

  //#endif

//Kill client and stop him form running once he ran the userprogram designed for him.
//#ifdef NETWORK
  // interrupt->Halt();
  //#endif
}

//Function thats used instead of repitiously copying it into each syscall.  Take in the message, create the proper
//packetheaders and mailheaders then send it out. This message, msg, will be the one that has the type and ids and Lock/Condtion 
//in it. Return if there was successful transmission or not.
#ifdef NETWORK
bool sendMail(char * msg){
  
  //Each process has it's own MAILBOX NUMBER
  Process *threadprocess = processTable->getProcess(currentThread->space);


  //Packet Header = Machine IDs
  //Ex. Client to Server
  //PacketHeader.to = Server machine id
  //PacketHeader.from = Client machine id
  PacketHeader outPktHdr, inPktHdr;
  int num = rand() % totalServerNum;
  outPktHdr.to = num;
  printf("Sending to Server #%d\n\n",num);

  //Mail Header = Mail Box Number
  //Ex. Client to Server
  //MailHeader.to = Server's mailbox # (It's ALWAYS 0)
  //MailHeader.from = Client's process mail box #
  MailHeader outMailHdr, inMailHdr;	
  outMailHdr.to = 0;
  outMailHdr.from = threadprocess->getMailBox();

  char buffer[MaxMailSize];	

  outMailHdr.length = strlen(msg) + 1;
  //printf(" OutPktHdr.to is %d,OutMailHdr.to is %d and data is %s\n",outPktHdr.to,outMailHdr.to,msg);							 
  // Send the first message
  bool success = postOffice->Send(outPktHdr, outMailHdr, msg);
  if ( !success ) {
    printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
    interrupt->Halt();
  }
  return success;
}
#endif

//Recieve mail  from the server and take the buffer, and parsethe message in order to figure out repsonse.
#ifdef NETWORK
int recieveMail(char buffer[])
{
  char *tokens;
  int response;
  char *types = new char[sizeof(buffer)];

  for ( tokens = strtok(buffer," "); tokens != NULL; tokens = strtok(NULL, " ") )
	  {
		  types = tokens;
		  response = atoi(types);
		  printf("here's the response:%d\n", response);
	  }
  fflush(stdout);
  return response;
}
#endif

//CreateLock syscall where you create the message 0 Lock id then pass it on to the server and wait for response.
//The  response will have  the id.
//int CreateLock_Syscall(int name){
int CreateLock_Syscall(int vaddr, int len){
#ifdef NETWORK
  char *bufs = new char[len];
  if( copyin(vaddr,len,bufs) == -1 ) {
    printf("%s","Exec_Syscall: Bad pointer passed to Open\n");
    delete bufs;
    return -1;
  }
  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char *data = new char[MaxMailSize];
  char *ack = "Default ACK";
  fflush(stdout);
  sprintf(data, "0 Lock %s",bufs );
  char buffer[MaxMailSize];
  
  // Send the first message
  bool success = sendMail(data);
  if ( !success ) {
    printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
    interrupt->Halt();
  }else{
    printf("CreateLock: The message was sent successfully.\n");
  }
  
  

  // Wait for the first message from the other machine
  postOffice->Receive(processTable->getProcess(currentThread->space)->getMailBox(),&inPktHdr, &inMailHdr, buffer);
  printf("AAA HERE'S THE BUFFER IN CLIENt RESPONSE:%s\n", buffer);
  int response = recieveMail(buffer);
  printf("\nCREATE NETWORKLOCK # %d: SUCCEEDED\n\n", response);
  delete[] bufs;
  return response;  
#endif
#ifndef NETWORK
  return synchTable->addLock();
#endif
}

//Destroy the lock takes in the index of the lock that the user wants todestroy. Take it form a message
// 1 Lock id then send tothe server.  WAit for a resposne, if response -2 then successful deletion else error and problem.
void DestroyLock_Syscall(int index){
#ifdef NETWORK
  printf("\nDestroyLock_Syscall(int index) in network directory.  Index Number = %d\n",index);
  char *data = new char[MaxMailSize];
  char *ack = "Default ACK";
  fflush(stdout);
  sprintf(data, "1 Lock %d", index);

  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char buffer[MaxMailSize];
  // Send the first message
  
  bool success = sendMail(data);
    if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }else{
      printf("DestroyLock:Seccessfully sent message\n");
    }
   
    // Wait for the first message from the other machine
    postOffice->Receive(processTable->getProcess(currentThread->space)->getMailBox(),&inPktHdr, &inMailHdr, buffer);
    int response = recieveMail(buffer);
    fflush(stdout);
    if(response == -1){
      printf("\nDESTROY LOCK # %d: FAILED\n\n",index);
    }
    else if(response == -2)
      {
	printf("\nDESTROY LOCK # %d: SUCCEEDED\n\n",index);
      }

    else
      {
	printf("Error Response:%d \n", response);
      }
#endif
#ifndef NETWORK
  synchTable->removeLock(index);
#endif
}



//Acqurie a lock takes in the index of the lock and then creates a message of the form 2 Lock id then send tothe server
//where you wait. If response isn't immediate then you need to wait because the lock  is being used by somebody but once
//opnededthen you will get the lock.
void AcquireLock_Syscall(int index){
#ifdef NETWORK
  char *data = new char[MaxMailSize];
  char *ack = "Default ACK";
  fflush(stdout);
  printf("AcquireLock: index number = %d\n", index);
  sprintf(data, "2 Lock %d", index);

  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char buffer[MaxMailSize];
  // Send the first message
  
  bool success = sendMail(data); 
    if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }else{
      printf("The message was sent successfully\n");
    }
    
    // Wait for the first message from the other machine
    postOffice->Receive(processTable->getProcess(currentThread->space)->getMailBox(),&inPktHdr, &inMailHdr, buffer);
    printf("Got \"%s\" from %d, box %d in the exception\n",buffer,inPktHdr.from,inMailHdr.from);
    int response = recieveMail(buffer);
    fflush(stdout);
     if(response == -1){
      printf("\nACQUIRED LOCK # %d: FAILED\n\n",index);
    }
    else if(response == -2)
      {
	printf("\nACQUIRED LOCK # %d: SUCCEEDED\n\n",index );
      }
    else
      {
	printf("Error response:%d \n", response);
      }
#endif
#ifndef NETWORK
  synchTable->acquireLock(index);
#endif
}


//Release lock takes in an index then send message to the server of form 3 Lock id then  await resposne. If -2 then successful else 
//error some form arised.
void ReleaseLock_Syscall(int index){
#ifdef NETWORK
  printf("\nRelease_Syscall(int index) in network directory.  Index Number = %d\n",index);
  char *data = new char[MaxMailSize];
  char *ack = "Default ACK";
  fflush(stdout);
  sprintf(data, "3 Lock %d", index);

  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char buffer[MaxMailSize];
  // Send the first message
  bool success = sendMail(data);
    if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }else{
      printf("ReleaseLock:Seccessfully sent message\n");
    }
   
    // Wait for the first message from the other machine
    postOffice->Receive(processTable->getProcess(currentThread->space)->getMailBox(),&inPktHdr, &inMailHdr, buffer);
    int response = recieveMail(buffer);
    fflush(stdout);
    if(response == -1){
      printf("\nRELEASE LOCK # %d: FAILED\n\n",index);
    }
    else if(response == -2)
      {
	printf("\nRELEASE LOCK # %d: SUCCEEDED\n\n",index);
      }
    else
      {
	printf("Error response:%d \n", response);
      }
#endif
#ifndef NETWORK
  synchTable->releaseLock(index);
#endif
}


//CreateCondition syscall where you create the message 4 Condition id then pass it on to the server and wait for response.
//The  response will have  the id.
int CreateCondition_Syscall(int vaddr, int len){
#ifdef NETWORK
  printf("CreateCondition_Syscall() in network directory\n");
  char *bufs = new char[len];
   if( copyin(vaddr,len,bufs) == -1 ) {
	printf("%s","Exec_Syscall: Bad pointer passed to Open\n");
	delete bufs;
	return -1;
      }
    PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char *data = new char[MaxMailSize];
  char *ack = "Default ACK";
  fflush(stdout);
  sprintf(data, "4 Condition %s",bufs );
  char buffer[MaxMailSize];

  // Send the first message
  bool success = sendMail(data);
  if ( !success ) {
    printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
    interrupt->Halt();
  }else{
    printf("NETWORKCreateCOND: The message was sent successfully.\n");
  }
   
    // Wait for the first message from the other machine
  postOffice->Receive(processTable->getProcess(currentThread->space)->getMailBox(),&inPktHdr, &inMailHdr, buffer);
  int response = recieveMail(buffer);
  printf("\nCREATE NETWORKCONDITION # %d: SUCCEEDED\n\n", response);
  delete[] bufs;
  return response;


#endif
#ifndef NETWORK
  return synchTable->addCondition();
#endif
}



//Destroy the lock takes in the index of the condition that the user wants todestroy. Take it form a message
// 5 Condition id then send tothe server.  WAit for a resposne, if response -2 then successful deletion else error and problem.
void DestroyCondition_Syscall(int index){
#ifdef NETWORK
  printf("\nDestroyCondition_Syscall(int index) in network directory.  Index Number = %d\n",index);
  char *data = new char[MaxMailSize];
  char *ack = "Default ACK";
  fflush(stdout);
  sprintf(data, "5 Condition %d", index);

  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char buffer[MaxMailSize];
  // Send the first message
  bool success = sendMail(data);
    if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }else{
      printf("DestroyCondition:Seccessfully sent message\n");
    }   
    // Wait for the first message from the other machine
    postOffice->Receive(processTable->getProcess(currentThread->space)->getMailBox(),&inPktHdr, &inMailHdr, buffer);
    int response = recieveMail(buffer);
    fflush(stdout);
    if(response == -1){
      printf("\nDESTROY CONDITION # %d: FAILED\n\n",index);
    }
    else if(response == -2)
      {
	printf("\nDESTROY CONDITION # %d: SUCCEEDED\n\n",index);
      }
    else
      {
	printf("Error response:%d \n", response);
      }
#endif
#ifndef NETWORK
  synchTable->removeCondition(index);
#endif
}



//Wait condition takes in the lock and ocndition syscall then it sends the message tot he serverof theform 
//6 Condition id Lock id. Then it ends up waiting,so "freezing" in sense becausenow he waits till signalled
//from server that the lock under this condition is given  up and can now be given to me. Then the server
//will s end the message tot he client that here it is.
void WaitCondition_Syscall(int c_index, int l_index){
#ifdef NETWORK
  printf("\nWaitCondition_Syscall() in #ifdef NETWORK\n");
  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char *data = new char[MaxMailSize];
  char *ack = "Default ACK";
  fflush(stdout);
  sprintf(data, "6 Condition %d Lock %d", c_index, l_index);
  char buffer[MaxMailSize];

  // Send the first message
  bool success = sendMail(data);
  if ( !success ) {
    printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
    interrupt->Halt();
  }else{
    printf("WaitCondition: The message was sent successfully.\n");
  }
  // Wait for the first message from the other machine
  postOffice->Receive(processTable->getProcess(currentThread->space)->getMailBox(),&inPktHdr, &inMailHdr, buffer);
  int response = recieveMail(buffer);
    if(response == -1){
      printf("\nWAIT CONDITION # %d WITH LOCK # %d: FAILED\n\n",c_index,l_index);
    }
    else if(response == -2)
      {
	printf("\nWAIT CONDITION # %d WITH LOCK # %d: SUCCEEDED\n\n",c_index,l_index);
      }
    else
      {
	printf("ERROR response:%d\n", response);
      }
    return;
#endif
#ifndef NETWORK
  synchTable->waitCondition(c_index, l_index);
#endif
}



//Signal will take in the condition and lock index.Then send out to the server  a message of the form 7 Condition id Lock id where you wait for 
//a response from the server; a -2 signifies positive success whereas a -1 shows failure  or something wrong.
void SignalCondition_Syscall(int c_index, int l_index){
#ifdef NETWORK
  printf("\nSignalCondition_Syscall() in #ifdef NETWORK\n");
  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char *data = new char[MaxMailSize];
  char *ack = "Default ACK";
  fflush(stdout);
  sprintf(data, "7 Condition %d Lock %d", c_index, l_index);
  char buffer[MaxMailSize];
  // Send the first message
  bool success = sendMail(data);
  if ( !success ) {
    printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
    interrupt->Halt();
  }else{
    printf("SignalCondition: The message was sent successfully.\n");
  }
      // Wait for the first message from the other machine
  printf("SignalCondition: right before Receive\n");
  postOffice->Receive(processTable->getProcess(currentThread->space)->getMailBox(),&inPktHdr, &inMailHdr, buffer);
  int response = recieveMail(buffer);
    if(response == -1){
      printf("\nSIGNAL CONDITION # %d WITH LOCK # %d: FAILED\n\n",c_index,l_index);
    }
    else if(response == -2)
      {
	printf("\nSIGNAL CONDITION # %d WITH LOCK # %d: SUCCEEDED\n\n",c_index,l_index);
      }
    else
      {
	printf("ERROR response:%d \n", response);
      }
#endif
#ifndef NETWORK
  synchTable->signalCondition(c_index, l_index);
#endif
}


//Broadcast will take in the condition and lock index.Then send out to the server  a message of the form 8 Condition id Lock id where you wait for 
//a response from the server; a -2 signifies positive success whereas a -1 shows failure  or something wrong.
void BroadcastCondition_Syscall(int c_index, int l_index){
#ifdef NETWORK
  printf("\nBroadCastCondition_Syscall() in #ifdef NETWORK\n");
  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char *data = new char[MaxMailSize];
  char *ack = "Default ACK";
  fflush(stdout);
  sprintf(data, "8 Condition %d Lock %d", c_index, l_index);
  char buffer[MaxMailSize];
  // Send the first message
  bool success = sendMail(data);
  if ( !success ) {
    printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
    interrupt->Halt();
  }else{
    printf("BroadCastCondition: The message was sent successfully.\n");
  }
    // Wait for the first message from the other machine
  printf("BroadCastCondition: right before Receive\n");
  postOffice->Receive(processTable->getProcess(currentThread->space)->getMailBox(),&inPktHdr, &inMailHdr, buffer);
  int response = recieveMail(buffer);
    if(response == -1){
      printf("\nBROADCAST CONDITION # %d WITH LOCK # %d: FAILED\n\n",c_index,l_index);
    }
    else if(response == -2)
      {
	printf("\nBROADCAST CONDITION # %d WITH LOCK # %d: SUCCEEDED\n\n",c_index,l_index);
      }
    else
      {
	printf("ERROR response:%d \n", response);
      }
#endif
#ifndef NETWORK

  synchTable->broadcastCondition(c_index, l_index);

#endif
}



/*
  ----------------------------------
  * 
  *    OBLETE SYSTEM CALLS!!!
  ----------------------------------
*/



int incrementEntityCount_Syscall(int entityType){
#ifdef NETWORK
  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char *data = new char[MaxMailSize];
  char *ack = "Default ACK";
  fflush(stdout);
  sprintf(data, "9 %d", entityType);
  char buffer[MaxMailSize];
  // Send the first message
  bool success = sendMail(data);
  if ( !success ) {
    printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
    interrupt->Halt();
  }else{
    printf("incrementEntityCount: The message was sent successfully.\n");
  }
      // Wait for the first message from the other machine
  printf("SetClerkData: right before Receive\n");
  postOffice->Receive(processTable->getProcess(currentThread->space)->getMailBox(),&inPktHdr, &inMailHdr, buffer);
  int response = recieveMail(buffer);
  printf("incrementEntityCount_Syscall succeeded\n");
  return response;
#endif
}



void decrementEntityCount_Syscall(){
#ifdef NETWORK
  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char *data = new char[MaxMailSize];
  char *ack = "Default ACK";
  fflush(stdout);
  sprintf(data, "10");
  char buffer[MaxMailSize];
  // Send the first message
  bool success = sendMail(data);
  if ( !success ) {
    printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
    interrupt->Halt();
  }else{
    printf("decrementEntityCount: The message was sent successfully.\n");
  }
      // Wait for the first message from the other machine
  printf("SetClerkData: right before Receive\n");
  postOffice->Receive(processTable->getProcess(currentThread->space)->getMailBox(),&inPktHdr, &inMailHdr, buffer);
  int response = recieveMail(buffer);
  if(response == -2){
    printf(" decrementEntityCount_Syscall succeeded\n");
  }
  else {
    printf("decrementEntityCount_Syscall failed\n");
  }
#endif
}



void setClerkData_Syscall(int clerktype,int datatype, int value){
#ifdef NETWORK
  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char *data = new char[MaxMailSize];
  char *ack = "Default ACK";
  fflush(stdout);
  sprintf(data, "11 %d %d %d", clerktype, datatype,value);
  char buffer[MaxMailSize];
  // Send the first message
  bool success = sendMail(data);
  if ( !success ) {
    printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
    interrupt->Halt();
  }else{
    printf("SetClerkData: The message was sent successfully.\n");
  }
      // Wait for the first message from the other machine
  printf("SetClerkData: right before Receive\n");
  postOffice->Receive(processTable->getProcess(currentThread->space)->getMailBox(),&inPktHdr, &inMailHdr, buffer);
  int response = recieveMail(buffer);
  if(response == -2){
    printf("SetClerkData_Syscall succeeded\n");
  }
  else {
    printf("SetClerkData_Syscall failed\n");
  }

#endif
}

int getClerkData_Syscall(int clerktype,int datatype){
#ifdef NETWORK
  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char *data = new char[MaxMailSize];
  char *ack = "Default ACK";
  fflush(stdout);
  sprintf(data, "12 %d %d", clerktype,datatype);
  char buffer[MaxMailSize];

  // Send the first message
  bool success = sendMail(data);
  if ( !success ) {
    printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
    interrupt->Halt();
  }else{
    printf("getClerkData: The message was sent successfully.\n");
  }
   
  // Wait for the first message from the other machine
  postOffice->Receive(processTable->getProcess(currentThread->space)->getMailBox(),&inPktHdr, &inMailHdr, buffer);
  int response = recieveMail(buffer);
  printf("\ngetClerkData with value %d: SUCCEEDED\n\n", response);
  return response;
#endif
  return 0;
}



void setFiledData_Syscall(int fileDataType,int SSN){
#ifdef NETWORK
  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char *data = new char[MaxMailSize];
  char *ack = "Default ACK";
  fflush(stdout);
  sprintf(data, "13 %d %d", fileDataType,SSN);
  char buffer[MaxMailSize];
  // Send the first message
  bool success = sendMail(data);
  if ( !success ) {
    printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
    interrupt->Halt();
  }else{
    printf("setFileData The message was sent successfully.\n");
  }
      // Wait for the first message from the other machine
  printf("setFileData: right before Receive\n");
  postOffice->Receive(processTable->getProcess(currentThread->space)->getMailBox(),&inPktHdr, &inMailHdr, buffer);
  int response = recieveMail(buffer);
  if(response == -2){
    printf("SetFileData_Syscall succeeded\n");
  }
  else {
    printf("SetFileData_Syscall failed\n");
  }

#endif
}



int getFiledData_Syscall(int fileDataType){
#ifdef NETWORK
  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char *data = new char[MaxMailSize];
  char *ack = "Default ACK";
  fflush(stdout);
  sprintf(data, "14 %d", fileDataType);
  char buffer[MaxMailSize];
  // Send the first message
  bool success = sendMail(data);
  if ( !success ) {
    printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
    interrupt->Halt();
  }else{
    printf("setFileData The message was sent successfully.\n");
  }
      // Wait for the first message from the other machine
  printf("getFileData: right before Receive\n");
  postOffice->Receive(processTable->getProcess(currentThread->space)->getMailBox(), &inPktHdr, &inMailHdr, buffer);
  int response = recieveMail(buffer);
  printf("getFileData_Syscall succeeded\n");
  return response;
#endif
}



/***************************

   NETWORK PASSPORT SYSTEM CALLS

***************************/


//Register the client's process to the server
int RegisterMe_Syscall(int type){
#ifdef NETWORK

  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;

  char *data = new char[MaxMailSize];
  char *ack = "Default ACK";
  fflush(stdout);

  //Make the message with the type of the user program
  sprintf(data, "15 %d", type);
  char buffer[MaxMailSize];

  // Send the first message
  bool success = sendMail(data);
  if ( !success ) {
    printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
    interrupt->Halt();
  }else{
    printf("RegisterMe: called and the message was sent successfully (msg: %s)\n",data);
  }

  // Wait for the first message from the other machine
  //Give the post office the current process's mail box number to retrieve the correct mail
  postOffice->Receive(processTable->getProcess(currentThread->space)->getMailBox(),&inPktHdr, &inMailHdr, buffer);
  //Parse the received mail for the response
  int response = recieveMail(buffer);

  printf("RegisterMe: Received response - index %d\n", response);

  return response;
#endif
}

int PlaceMeInLine_Syscall(int index, int line){
#ifdef NETWORK

  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;

  char *data = new char[MaxMailSize];
  char *ack = "Default ACK";
  fflush(stdout);

  //Make the message with the user program's index, line type, and id for the system call
  sprintf(data, "16 %d %d", index, line);
  char buffer[MaxMailSize];

  // Send the first message
  bool success = sendMail(data);
  if ( !success ) {
    printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
    interrupt->Halt();
  }else{
    printf("PlaceMeInLine: called and the message was sent successfully (msg: %s)\n",data);
  }

  // Wait for the first message from the other machine
  //Give the post office the current process's mail box number to retrieve the correct mail
  postOffice->Receive(processTable->getProcess(currentThread->space)->getMailBox(),&inPktHdr, &inMailHdr, buffer);
  //Parse the received mail for the response
  int response = recieveMail(buffer);

  printf("PlaceMeInLine: Received response %d\n", response);
  fflush(stdout);
  
  return response;
#endif
}

int RemoveCustomer_Syscall(int index, int line){
  //printf("RemoveCustomer CALLED with index %d, line %d!\n", index, line);
#ifdef NETWORK

  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;

  char *data = new char[MaxMailSize];
  char *ack = "Default ACK";
  fflush(stdout);

  //Make the message with the user program's index, line type, and id for the system call
  sprintf(data, "17 %d %d", index, line);
  char buffer[MaxMailSize];

  // Send the first message
  bool success = sendMail(data);
  if ( !success ) {
    printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
    interrupt->Halt();
  }else{
    printf("RemoveCustomer: called and the message was sent successfully (msg: %s)\n", data);
  }

  // Wait for the first message from the other machine
  //Give the post office the current process's mail box number to retrieve the correct mail
  postOffice->Receive(processTable->getProcess(currentThread->space)->getMailBox(),&inPktHdr, &inMailHdr, buffer);
  //Parse received mail for response
  int response = recieveMail(buffer);

  printf("RemoveCustomer: Received response %d\n", response);
  fflush(stdout);
  
  return response;
#endif
}

int MoneyCollected_Syscall(){
#ifdef NETWORK

  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;

  char *data = new char[MaxMailSize];
  char *ack = "Default ACK";
  fflush(stdout);

  //Make the message with the id for the system call
  sprintf(data, "18 %d", 18);
  char buffer[MaxMailSize];

  // Send the first message
  bool success = sendMail(data);
  if ( !success ) {
    printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
    interrupt->Halt();
  }else{
    printf("MoneyCollected: The message was sent successfully.\n");
  }
      // Wait for the first message from the other machine
  //printf("PlaceMeInLine: right before Receive\n");
  postOffice->Receive(processTable->getProcess(currentThread->space)->getMailBox(),&inPktHdr, &inMailHdr, buffer);
  int response = recieveMail(buffer);

  printf("MoneyCollected: Received response %d\n", response);
  fflush(stdout);
  
  return response;
#endif
}


/*
  -------------------------
  * 
  *    PAGEFAULT HANDLER 
  *
  -------------------------
*/


/*
  this method will look for a free ppn and if none is found it will evict 
  a page of memory and write it to the swap file and free a mainMemory page
*/
int findFreePPN(){

  //Searching for free main memory
  MMLock->Acquire();
  int ppn = mainMemory->Find();
  MMLock->Release();

  if(ppn != -1){
    //free main memory found! returning physical page number of main memory
    return ppn;
  }

  else {
    //main memory is full, need to evict a page and swap it out if necessary 

    //Use FIFO or Random?
    if(FIFO){
    //using FIFO
      printf("Process %d - Evicting a page through FIFO policy\n",currentThread->space);
      ipt->evictFirstSlot();
    }
    else{
    //using random
      printf("Process %d - Evicting a page through Random policy\n",currentThread->space);
      ipt->evictRandomSlot();
    }

    //then try again to get the main memory.
    MMLock->Acquire();
    ppn =  mainMemory->Find();
    MMLock->Release();

    return ppn;
  }
}

//Page fault exception handler for handling page faults 
void pageFaultException(){
  
  //look in page table for the vaddr causing the page fault
  unsigned vAddr = machine->ReadRegister(BadVAddrReg);
  int vpn = vAddr/PageSize; //Get the virtual page number
  
  //Validate the virtual address, make sure it is is within the bounds of the address space
  if(!currentThread->space->validateVaddr(vpn*PageSize)){ 
    //  printf("Process %d - page faulted, but vpn is too large or small\n", currentThread->space);
    interrupt->Halt();
    return;
  }
  
  //Acquire page fault lock to prevent race condition
  pfl.Acquire();

  //printf("Process %d - Raised PageFaultException for vAddr: %d\tvpn: %d\n",currentThread->space,vAddr,vpn);

  //Check whether the requested vpn is in main memory by looking in the IPT
  int ppn = ipt->findPPN(vpn,currentThread->space); 

  //IPT miss, not in  main memory
  if(ppn == -1) {

    //Page requested is in executable file
    if(currentThread->space->pageTableInfo[vpn].location == InExecutable){

      //    printf("Process %d - IPT MISS: for vpn %d and it is in executable.\n",currentThread->space,vpn); 

      //we need to load it from executeable into memory
      ppn = findFreePPN(); //Find a free page of main memory, evict one if necessary

      //Load the executable file page into main memory
      currentThread->space->execFile->ReadAt(&(machine->mainMemory[ppn*PageSize]),PageSize, currentThread->space->noffH.code.inFileAddr + PageSize*vpn);

      //update the new page entry to ipt
      ipt->setElement(vpn,ppn,currentThread->space);
    }

    //Page requested is in swap file
    else if(currentThread->space->pageTableInfo[vpn].location == InSwapFile){

      //  printf("Process %d - IPT MISS: for vpn %d and it is in swap file\n",currentThread->space,vpn); 
    
      //We need to load it from executable into memory
      ppn = findFreePPN(); //Find a free page of main memory, evict one if necessary
      
      //write the entry into memory
      int readLocation = currentThread->space->pageTableInfo[vpn].swapFilePosition;
      
      //old method required to free up the swapFile slot this was taking up
      //swapFileBitmap->Clear(readLocation);
      //currentThread->space->pageTableInfo[vpn].swapFilePosition = -1;

      //printf("reading for vpn %d from swapfile location %d\n",vpn,readLocation);
      swapFile->ReadAt(&(machine->mainMemory[PageSize*ppn]),PageSize,readLocation*PageSize);

      //printf("~~~~~~~ READ swap file at position %d for vpn %d of processID %d\n", readLocation, vpn, currentThread->space);
      

      //update the ipt
      ipt->setElement(vpn,ppn,currentThread->space);      
      ipt->setDirtyBit(vpn); //Ensure it will always write to swap file after reading from it     
    }
  
    else {
      //ERRORS: Debugging messages

      printf("IPT MISS: Where is it?");

      if(currentThread->space->pageTableInfo[vpn].location == InMainMemory){
	printf(" In main memory!\n");
      }
      else if(currentThread->space->pageTableInfo[vpn].location == None){
	printf(" No where?!\n");
      }
      else {
	printf(" Unknown enum value..\n");
      }
    }
  }
    
  if(ppn == -1){
    printf("Process %d : ppn is -1 for vpn %d!! page fault exception handler failed!\n",currentThread->space,vpn);
    interrupt->Halt();
    return;
  }

  //it must be in memory to go here
  if(currentThread->space->pageTableInfo[vpn].location == InMainMemory){ 
    //add entry to TLB

    int i = 0;

    //Which page replacement policy?
    if(FIFO){
      i = currentTLBSlot;
      
      if(currentTLBSlot == 3){
	currentTLBSlot = 0;
      }
      else
	{
	  currentTLBSlot++;
	}
    }
    else {
      i = rand()%4;
    }

    //Propogate tlb dirty entries to ipt table
    if(machine->tlb[i].valid){
      if( machine->tlb[i].dirty){
	ipt->setDirtyBit(machine->tlb[i].virtualPage);
      }
      if(machine->tlb[i].use)
        ipt->setUseBit(machine->tlb[i].virtualPage);
    }
    
 
    //Set the tlb values for the new page entry
    IntStatus oldLevel = interrupt->SetLevel(IntOff);
       
    machine->tlb[i].valid = true;
    machine->tlb[i].virtualPage = vpn;
    machine->tlb[i].physicalPage = ppn;
    machine->tlb[i].readOnly = false;
    machine->tlb[i].use = false;
    machine->tlb[i].dirty = false;
      
    //printf("Process %d: Page Fault Handled - TLB slot %d populated with vpn %d and ppn %d\n", currentThread->space, i, vpn, ppn);
    (void)interrupt->SetLevel(oldLevel);  

  }

  //printf("end page fault vpn %d and addrspace %d\n",vpn,currentThread->space);

  pfl.Release();
}


void ExceptionHandler(ExceptionType which) {
    int type = machine->ReadRegister(2); // Which syscall?
    int rv=0; 	// the return value from a syscall
    if(which == PageFaultException){
      DEBUG('a',"PageFault Exception\n");
      pageFaultException();
      return;
    }
    if ( which == SyscallException ) {
      switch (type) {
      default:
	DEBUG('a', "Unknown syscall - shutting down.\n");
      case SC_Halt:
	DEBUG('a', "Shutdown, initiated by user program.\n");
	interrupt->Halt();
	break;
      case SC_Create:
	DEBUG('a', "Create syscall.\n");
	Create_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
	break;
      case SC_Open:
	DEBUG('a', "Open syscall.\n");
	rv = Open_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
	break;
      case SC_Write:
	DEBUG('a', "Write syscall.\n");
	Write_Syscall(machine->ReadRegister(4),
		      machine->ReadRegister(5),
		      machine->ReadRegister(6));
	break;
      case SC_Read:
	DEBUG('a', "Read syscall.\n");
	rv = Read_Syscall(machine->ReadRegister(4),
			  machine->ReadRegister(5),
			  machine->ReadRegister(6));
	break;
      case SC_Close:
	DEBUG('a', "Close syscall.\n");
	Close_Syscall(machine->ReadRegister(4));
	break;
      case SC_Yield:
	DEBUG('a', "Yield syscall.\n");
	Yield_Syscall();
	break;
      case SC_Exec:
	DEBUG('a', "Execute syscall.\n");
	rv = 
	  Exec_Syscall(machine->ReadRegister(4),machine->ReadRegister(5));
	break;
      case SC_Fork:
	DEBUG('a',"Fork syscall.\n");
	Fork_Syscall(machine->ReadRegister(4));
	break;
      case SC_Exit:
	DEBUG('a',"Exit syscall.\n");
	printf("Exit value:%d\n", machine->ReadRegister(4));
	Exit_Syscall(machine->ReadRegister(4));
	break;
      case SC_CreateLock:
	DEBUG('a',"CreateLock syscall.\n");
	//rv=CreateLock_Syscall(machine->ReadRegister(4));
	rv=CreateLock_Syscall(machine->ReadRegister(4),machine->ReadRegister(5));
	break;
      case SC_DestroyLock:
	DEBUG('a',"DestroyLock syscall.\n");
	DestroyLock_Syscall(machine->ReadRegister(4));
	break;
      case SC_AcquireLock:
	DEBUG('a',"AcquireLock syscall.\n");
	AcquireLock_Syscall(machine->ReadRegister(4));
	break;
      case SC_ReleaseLock:
	DEBUG('a',"ReleaseLock syscall.\n");
	ReleaseLock_Syscall(machine->ReadRegister(4));
	break;
      case SC_CreateCondition:
	DEBUG('a',"CreateCondition syscall.\n");
	//rv=CreateCondition_Syscall(machine->ReadRegister(4));
	rv=CreateCondition_Syscall(machine->ReadRegister(4),machine->ReadRegister(5));
	break;
      case SC_DestroyCondition:
	DEBUG('a',"DestroyCondition syscall.\n");
	DestroyCondition_Syscall(machine->ReadRegister(4));
	break;
      case SC_WaitCondition:
	DEBUG('a',"WaitCondition syscall.\n");
	WaitCondition_Syscall(machine->ReadRegister(4),machine->ReadRegister(5));
	break;
      case SC_SignalCondition:
	DEBUG('a',"SignalCondition syscall.\n");
        SignalCondition_Syscall(machine->ReadRegister(4),machine->ReadRegister(5));
	break;
      case SC_BroadcastCondition:
	DEBUG('a',"BroadcastCondition syscall.\n");
	BroadcastCondition_Syscall(machine->ReadRegister(4),machine->ReadRegister(5));
	break;
	//system calls for corrdinating a passport office
	//systemcall for incrementing the number of total clerk or customer
	//the increment call also returns the current value before incrementing
      case SC_IncrementEntityCount:
	rv = incrementEntityCount_Syscall(machine->ReadRegister(4));
	break;
      case SC_DecrementEntityCount:
	decrementEntityCount_Syscall();
	break;
	//system calls for the get/set of the variables for each clerk
	//and customer
      case SC_GetClerkData:
	rv = getClerkData_Syscall(machine->ReadRegister(4),machine->ReadRegister(5));
	break;
      case SC_SetClerkData:
	printf("register values are %d %d %d\n",machine->ReadRegister(4),machine->ReadRegister(5),machine->ReadRegister(6));
	setClerkData_Syscall(machine->ReadRegister(4),
		     machine->ReadRegister(5),
		     machine->ReadRegister(6));
	break;
	//this system call sets the variables which keep track of which customer has what parts needed for the passport done
      case SC_SetFiledData:
	setFiledData_Syscall(machine->ReadRegister(4),machine->ReadRegister(5));
	break;
      case SC_GetFiledData:
	rv = getFiledData_Syscall(machine->ReadRegister(4));
	break;

      case SC_RegisterMe:
	rv = RegisterMe_Syscall(machine->ReadRegister(4));
	break;

      case SC_PlaceMeInLine:
	rv = PlaceMeInLine_Syscall(machine->ReadRegister(4),machine->ReadRegister(5));
	break;

      case SC_RemoveCustomer:
	rv = RemoveCustomer_Syscall(machine->ReadRegister(4),machine->ReadRegister(5));
	break;

      case SC_MoneyCollected:
	rv = MoneyCollected_Syscall();
	break;

      }
      // Put in the return value and increment the PC
      machine->WriteRegister(2,rv);
      machine->WriteRegister(PrevPCReg,machine->ReadRegister(PCReg));
      machine->WriteRegister(PCReg,machine->ReadRegister(NextPCReg));
      machine->WriteRegister(NextPCReg,machine->ReadRegister(PCReg)+4);
      return;
    } else {
      cout<<"Unexpected user mode exception - which:"<<which<<"  type:"<< type<<endl;
      interrupt->Halt();
    }
}
